package com.wimcorp.magic.dataloader.abilities;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.wimcorp.magic.data.AbilityData;
import com.wimcorp.magic.data.CostData;
import com.wimcorp.magic.dataloader.costs.CostWidget;
import com.wimcorp.magic.input.AbilityInput;
import com.wimcorp.magic.input.AbilityTriggerInput;
import com.wimcorp.magic.input.BehaviorInput;
import com.wimcorp.magic.input.ColorInput;
import com.wimcorp.magic.input.Condition;
import com.wimcorp.magic.input.CostInput;
import com.wimcorp.magic.input.Duration;
import com.wimcorp.magic.input.Location;
import com.wimcorp.magic.input.Target;
import com.wimcorp.magic.input.Type;

public class AbilityWidget {
	private String name;
	private Grid grid = new Grid(4, 6);
	private CellPanel configurationPanel = new VerticalPanel();
	private TextArea abilityDescription;
	private ListBox targetList;
	private ListBox triggerList;
	private ListBox durationList;
	private CheckBox locationOrCheckBox;
	private ListBox locationList;
	private ListBox typeList;
	private CheckBox typeOrCheckBox;
	private ListBox behaviorList;
	private CheckBox behaviorOrCheckBox;

	private VerticalPanel costPanel = new VerticalPanel();
	private Map<CostInput, CostWidget> costWidgets = new HashMap<>();

	public AbilityWidget(AbilityInput ability) {
		this.name = ability.toString();
		initAbilityFields();
	}

	public static AbilityWidget createAbilityWidget(AbilityInput ability) {
		switch (ability) {
		case ADD_BEHAVIOR:
			return new AddBehaviorWidget(ability);
		case ADD_CARD_COUNT_COUNTER:
			return new AddCardCountCounterWidget(ability);
		case ADD_COUNTER:
			return new AddCounterWidget(ability);
		case ADD_MANA_TO_POOL:
			return new AddManaToPoolWidget(ability);
		case COMBO_ABILITY:
			return new ComboAbilityWidget(ability);
		case CONDITION_ABILITY:
			return new ConditionAbilityWidget(ability);
		case COUNTER_SPELL:
			return new CounterSpellWidget(ability);
		case DAMAGE_TARGET:
			return new DamageTargetWidget(ability);
		case DRAW_CARDS:
			return new DrawCardsWidget(ability);
		case LIFE:
			return new LifeWidget(ability);
		case RETURN:
			return new ReturnWidget(ability);
		case REVEAL_DECK_AND_CHOOSE:
			return new RevealDeckAndChooseWidget(ability);
		case REVEAL_DECK_KEEP_TYPE_AND_RETURN_IN_ANY_ORDER:
			return new RevealDeckKeepTypeAndReturnInAnyOrderWidget(ability);
		case SCRY:
			return new ScryWidget(ability);
		case SUSPEND:
			return new SuspendWidget(ability);
		default:
			return new AbilityWidget(ability);
		}
	}

	public JSONObject getAbilityConfig() {
		JSONObject config = new JSONObject();
		JSONArray descriptionSet = new JSONArray();
		descriptionSet.set(0, new JSONString(abilityDescription.getText()));
		config.put("description", descriptionSet);
		config.put("target", getSelectedItems(targetList));
		config.put("trigger", getSelectedItems(triggerList));
		config.put("duration", getSelectedItems(durationList));
		config.put("locations", getMultiSelectedItems(locationList, locationOrCheckBox.isChecked()));
		config.put("types", getMultiSelectedItems(typeList, typeOrCheckBox.isChecked()));
		config.put("behaviors", getMultiSelectedItems(behaviorList, behaviorOrCheckBox.isChecked()));
		return config;
	}

	public void load(AbilityData abilityData) {

		JSONObject config = (JSONObject) JSONParser.parseLenient(abilityData.getConfig());
		abilityDescription.setText(toString(config.get("description")).replace("{", "(").replace("}", ")")
				.replace("\\n", " "));
		loadListBox(targetList, config.get("target"));
		loadListBox(triggerList, config.get("trigger"));
		loadListBox(durationList, config.get("duration"));

		loadMultiListBox(locationList, locationOrCheckBox, (JSONArray) config.get("locations"));
		loadMultiListBox(typeList, typeOrCheckBox, (JSONArray) config.get("types"));
		loadMultiListBox(behaviorList, behaviorOrCheckBox, (JSONArray) config.get("behaviors"));
		loadCosts(abilityData.getCosts());

	}

	private void loadCosts(Set<CostData> costs) {
		for (CostData cost : costs) {
			CostInput costInput = CostInput.valueOf(cost.getCost());
			CostWidget costWidget = CostWidget.createCostWidget(costInput);
			Iterator<Widget> iterator = costPanel.iterator();
			while (iterator.hasNext()) {
				Widget w = iterator.next();
				if (w instanceof CellPanel) {
					CellPanel checkBoxPanel = (CellPanel) w;
					Iterator<Widget> checkBoxPanelIterator = checkBoxPanel.iterator();
					while (checkBoxPanelIterator.hasNext()) {
						Widget checkBox = checkBoxPanelIterator.next();
						if (checkBox instanceof CheckBox) {
							if (((CheckBox) checkBox).getName().equals(cost.getCost())) {
								((CheckBox) checkBox).setValue(true);
								checkBoxPanel.add(costWidget.getConfigurationPanel());
							}
						}

					}
				}

			}
			costWidget.load(cost);
			costWidgets.put(costInput, costWidget);
		}
	}

	public String toString(JSONValue value) {
		if (value instanceof JSONArray) {
			return ((JSONArray) value).get(0).toString().replace("\"", "");
		} else {
			return value.toString().replace("\"", "");
		}
	}

	private void loadMultiListBox(ListBox listbox, CheckBox checkbox, JSONArray values) {
		Set<String> result = new HashSet<>();
		if (values.size() == 1) {
			String value = toString(values.get(0));
			if (value.contains("_OR_")) {

				String[] split = value.split("_OR_");
				for (String s : split) {
					result.add(s);
				}
				// check or box
				checkbox.setValue(true);
				for (int i = 0; i < listbox.getItemCount(); i++) {
					if (result.contains(listbox.getItemText(i)) || result.contains(listbox.getValue(i))) {
						listbox.setItemSelected(i, true);
					}
				}
			}

		}
		for (int i = 0; i < listbox.getItemCount(); i++) {
			for (int j = 0; j < values.size(); j++) {
				if (toString(values.get(0)).equals(listbox.getItemText(i)) || toString(values.get(0)).equals(listbox.getValue(i))) {
					listbox.setItemSelected(i, true);
				}
			}
		}

	}

	protected void loadListBox(ListBox listbox, JSONValue json) {
		if (json != null) {
			String value = toString(json);
			if (value != null) {
				for (int i = 0; i < listbox.getItemCount(); i++) {
					if (value.equals(listbox.getItemText(i)) || value.equals(listbox.getValue(i))) {
						listbox.setItemSelected(i, true);
					}
				}
			}
		}
	}

	protected CheckBox createCheckBox(String name, String label) {
		CheckBox checkBox = new CheckBox(label);
		checkBox.setName(name);
		return checkBox;
	}

	protected void initAbilityFields() {
		addAbilityDescription();
		configurationPanel.add(grid);
		addSingleAbilityTarget();
		addSingleAbilityTrigger();
		addSingleAbilityDuration();
		addMultiAbilityLocations();
		addMultiAbilityTypes();
		addMultiAbilityBehaviors();
		initCostPanel();
	}

	protected JSONArray getMultiSelectedItems(ListBox listbox, boolean or) {
		JSONArray selected = new JSONArray();
		if (listbox.isMultipleSelect()) {
			HashSet<Integer> indexes = new HashSet<Integer>();

			while (listbox.getSelectedIndex() >= 0) {
				int index = listbox.getSelectedIndex();
				listbox.setItemSelected(index, false);
				String selectedElem = listbox.getValue(index);
				selected.set(indexes.size(), new JSONString(selectedElem));
				indexes.add(index);
			}

			if (or && selected.size() > 0) {
				String orSelection = "";
				for (int i = 0; i < selected.size(); i++) {
					orSelection += (toString(selected.get(i)) + "_OR_");
				}
				selected = new JSONArray();
				selected.set(0, new JSONString(orSelection.substring(0, orSelection.length() - 4)));
			}
			for (Integer index : indexes) {
				listbox.setItemSelected(index, true);
			}
		} else {
			selected.set(0, new JSONString(listbox.getValue(listbox.getSelectedIndex())));
		}
		return selected;
	}

	protected JSONArray getSelectedItems(ListBox listbox) {
		JSONArray selected = new JSONArray();
		if (listbox.isMultipleSelect()) {
			HashSet<Integer> indexes = new HashSet<Integer>();
			while (listbox.getSelectedIndex() >= 0) {
				int index = listbox.getSelectedIndex();
				listbox.setItemSelected(index, false);
				String selectedElem = listbox.getValue(index);
				selected.set(indexes.size(), new JSONString(selectedElem));
				indexes.add(index);
			}
			for (Integer index : indexes) {
				listbox.setItemSelected(index, true);
			}
		} else {
			selected.set(0, new JSONString(listbox.getValue(listbox.getSelectedIndex())));
		}
		return selected;
	}

	private void addAbilityDescription() {
		HorizontalPanel descriptionPanel = new HorizontalPanel();
		abilityDescription = new TextArea();
		descriptionPanel.add(new Label("Description"));
		abilityDescription.setHeight("75px");
		abilityDescription.setWidth("425px");
		descriptionPanel.add(abilityDescription);
		configurationPanel.add(descriptionPanel);

	}

	private void addSingleAbilityTarget() {
		grid.setWidget(0, 0, new Label("Target"));
		targetList = new ListBox();
		for (Target target : Target.values()) {
			targetList.addItem(target.getName(), target.toString());
		}
		grid.setWidget(0, 1, targetList);
		targetList.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				int selectedIndex = targetList.getSelectedIndex();
				if (Target.THIS.toString().equals(targetList.getValue(selectedIndex))) {
					grid.getRowFormatter().setVisible(1, false);
				} else {
					grid.getRowFormatter().setVisible(1, true);
				}
			}
		});
	}

	private void addSingleAbilityTrigger() {
		grid.setWidget(0, 2, new Label("Trigger"));
		triggerList = new ListBox();
		for (AbilityTriggerInput trigger : AbilityTriggerInput.values()) {
			triggerList.addItem(trigger.getName(), trigger.toString());
		}
		grid.setWidget(0, 3, triggerList);
	}

	private void addSingleAbilityDuration() {
		grid.setWidget(0, 4, new Label("Duration"));
		durationList = new ListBox();
		for (Duration duration : Duration.values()) {
			durationList.addItem(duration.getName(), duration.toString());
		}
		grid.setWidget(0, 5, durationList);
	}

	private void addMultiAbilityLocations() {
		VerticalPanel labelPanel = new VerticalPanel();
		labelPanel.add(new Label("Locations"));
		locationOrCheckBox = new CheckBox("Or");
		labelPanel.add(locationOrCheckBox);
		grid.setWidget(1, 0, labelPanel);
		locationList = new ListBox(true);
		for (Location location : Location.values()) {
			locationList.addItem(location.getName(), location.toString());
		}
		locationList.setVisibleItemCount(locationList.getItemCount());
		grid.setWidget(1, 1, locationList);
	}

	private void addMultiAbilityTypes() {
		VerticalPanel labelPanel = new VerticalPanel();
		labelPanel.add(new Label("Types"));
		typeOrCheckBox = new CheckBox("Or");
		labelPanel.add(typeOrCheckBox);
		grid.setWidget(1, 2, labelPanel);
		typeList = new ListBox(true);
		for (Type type : Type.values()) {
			typeList.addItem(type.getName(), type.toString());
		}
		typeList.setVisibleItemCount(typeList.getItemCount());
		grid.setWidget(1, 3, typeList);
	}

	private void addMultiAbilityBehaviors() {
		VerticalPanel labelPanel = new VerticalPanel();
		labelPanel.add(new Label("Behaviors"));
		behaviorOrCheckBox = new CheckBox("Or");
		labelPanel.add(behaviorOrCheckBox);
		grid.setWidget(1, 4, labelPanel);
		behaviorList = new ListBox(true);
		for (BehaviorInput behavior : BehaviorInput.values()) {
			behaviorList.addItem(behavior.getName(), behavior.toString());
		}
		behaviorList.setVisibleItemCount(behaviorList.getItemCount());
		grid.setWidget(1, 5, behaviorList);
	}

	protected void addInputSingleAbilityBehaviorList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (BehaviorInput behavior : BehaviorInput.values()) {
			list.addItem(behavior.getName(), behavior.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputSingleLocationList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (Location location : Location.values()) {
			list.addItem(location.getName(), location.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputSingleConditionList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (Condition condition : Condition.values()) {
			list.addItem(condition.getName(), condition.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputSingleTypeList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (Type type : Type.values()) {
			list.addItem(type.getName(), type.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputMultipleColorList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (ColorInput color : ColorInput.values()) {
			list.addItem(color.getColor(), color.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputSingleTargetList(String label, ListBox list, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		for (Target target : Target.values()) {
			list.addItem(target.getName(), target.toString());
		}
		grid.setWidget(2, 1 + (column * 2), list);
	}

	protected void addInputTextBox(String label, TextBox widget, int column) {
		grid.setWidget(2, 0 + (column * 2), new Label(label));
		grid.setWidget(2, 1 + (column * 2), widget);
	}

	protected void addInputSingleConditionOption(ListBox list) {
		HorizontalPanel inputPanel = new HorizontalPanel();
		inputPanel.add(new Label("Condition"));
		for (Condition condition : Condition.values()) {
			list.addItem(condition.getName(), condition.toString());
		}
		inputPanel.add(list);
		configurationPanel.add(inputPanel);
	}

	private void initCostPanel() {
		grid.setWidget(3, 0, new Label("Costs"));
		costPanel.setStyleName("costPanel");
		for (final CostInput cost : CostInput.values()) {
			final VerticalPanel panel = new VerticalPanel();
			final CheckBox costCheckBox = createCheckBox(cost.toString(), cost.getName());
			panel.add(costCheckBox);
			costCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
				@Override
				public void onValueChange(ValueChangeEvent<Boolean> event) {
					if (costCheckBox.getValue()) {
						// initCostFields(panel, costCheckBox);
						CostWidget costWidget = costWidgets.get(cost);
						if (costWidget == null) {
							costWidget = CostWidget.createCostWidget(cost);
							costWidgets.put(cost, costWidget);
						}
						panel.add(costWidget.getConfigurationPanel());
					} else {
						costWidgets.remove(cost);
						Widget w = panel.getWidget(0);
						panel.clear();
						panel.add(w);
					}
				}
			});

			costPanel.add(panel);
		}
		grid.setWidget(3, 1, costPanel);
	}

	public CellPanel getConfigurationPanel() {
		return configurationPanel;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Set<CostData> getCosts() {
		Set<CostData> costs = new HashSet<>();
		for (CostWidget cost : costWidgets.values()) {
			costs.add(new CostData(cost.getName(), cost.getConfig()));
		}
		return costs;
	}

}
